每一種題目可能有數種的解法,那我們應該怎麼評估每種解法的優劣呢?以前的我應該會回答,當然是越簡短的寫法越好呀! 不過,寫過leetcode之後,會發現有時候很簡短的解法,執行效率反而看不見別人的車尾燈(吊車尾),那麼leetcode是怎麼評斷解法的好壞呢? 答案是透過時間複雜度和空間複雜度來評估。
這邊要特別注意,執行的時間並非以秒為單位,因為家用電腦與超級電腦的運算速度根本天差地遠,比較秒數的話其實沒有多大的意義,因此是用次數來作計算,而我們習慣用O來表示演算法的執行效率,作為評估演算法執行效率好不好的指標,當函式輸入的Input 長度變長的時候,就可以了解演算法的效率趨勢,效能差的演算法的曲線就會陡峭上升像是O(n²)而好的演算法的曲線會趨於平緩O(log n)。
*圖片來源:*https://smootok.com/big-o-notation/
不管輸入的n為多少,都只要執行一次
//無論arr的length有多長,都只要執行一次即可
//ex : 找出index=2的值為何?
const arr = ['andy', 'peggy', 'debby']
console.log(arr[2])
輸入8的話,log 8 = 3,也就是8 = 2³ (8是n的3次方),所以經過3個步驟就可以找到我們要的答案,簡單來說就是不斷的剖半,像是二分搜尋法
跑一次迴圈,所以如果輸入的n越長,執行的次數也會等比增加
//找出這個陣列有沒有debby這個人,不用indexOf和includes話,就是土法煉鋼,從頭找到尾,一個一個慢慢找
const arr = ['andy', 'peggy', 'debby']
for(let i=0;i<arr.length;i++){
if(arr[i]==='debby') return true;
}
ex:快速排序法,在之後的文章會有更詳細的解說
ex:用雙迴圈印出一個九九乘法表
//n*n=n²
for(let i=1; i<=9; i++){
for(let j=i; j<=9; j++){
console.log(`${i}*${j} = ${i*j}`)
}
}
ex:n階乘,ex: n x n-1 x … x 3 x 2 x 1
最理想的時間複雜度就是O(log n)或是O(1)
執行程式佔用的記憶體空間 ,占用越多越吃資源,一樣也是用Big O來表示,而時間複雜度和空間複雜度兩者是可以互相trade off的。
有時候可以用空間來換取時間 ,或是用時間換取空間,依照當時的使用情境來做取捨。
O(1)
不管輸入的n為多少,都只會建立一個變數total,因此空間複雜度為O(n)
const add = (n) =>{
let total = 0
for(let i=0;i<n;i++){
total += i
}
return total
}
O(n)
arr的長度會根據使用者輸入的n來增長,因此空間複雜度為O(n)
const createArray = (n) =>{
const arr = []
for(let i=0;i<n;i++){
arr.push(i);
}
return arr
}
空間複雜度 看完還是不太了解 所以他的判定方式跟時間複雜度會是一樣嗎?
可以這麼理解
時間複雜度- 運算花了多少時間
空間複雜度- 運算占用多少記憶體
以生活來舉例
我要花多少時間才能搬完這些貨 - 時間複雜度
我要開幾台貨車才能載完這些貨 - 空間複雜度
以上面空間複雜度複雜度來說
重點會在let total = 0, const arr = []
這邊都是額外宣告一個變數來記錄運算結果
就會占用到記憶體
不知道這樣說明有沒有幫助你理解
這樣有讓我還是蠻困惑耶 所以只要有宣告都算是嗎
這樣他宣告到200多行 這樣到底複雜度又是什麼?
我只到佔用記憶體,但是我比較想說 他的宣告該不會是說
會有些是彈性宣告嗎??
並不是說只要宣告就是
其實跟宣告變數的數量沒有甚麼關係
要看你程式在運作的時候對這個變數塞了多少東西進去
假設現在有個演算法函式
裡面會宣告一個陣列來暫存運算結果
第一種演算法函式
如果帶入參數n
陣列長度永遠為1
這樣空間複雜度就是O(1)
function fn1(n){
//演算法邏輯會更改arr...
const arr = [0]
}
fn1(10);
第二種演算法函式
如果帶入參數n
陣列的長度就會變成n
這樣空間複雜度就是O(n)
function fn2(n){
//演算法邏輯會更改arr...
const arr = [0, 1, ...9] //arr.length = 10
}
fn2(10);
第三種演算法函式
如果帶入參數n
陣列的長度就會變成n²
這樣空間複雜度就是O(n²)
function fn3(n){
//演算法邏輯會更改arr...
const arr = [0, 1, ...99] //arr.length = 100
}
fn3(10);
重點就在於帶入的參數會讓空間複雜度如何成長